using System;
using System.Collections.Generic;
using System.Linq;
using AutoTest.Core.Infrastructure.IoC;

namespace AutoTest.Core.Infrastructure.Messaging
{
    public class MessageBus : IMessageBus
    {
        private readonly IServiceLocator _serviceLocator;

        public MessageBus(IServiceLocator serviceLocator)
        {
            _serviceLocator = serviceLocator;
        }

        public void Publish<T>(object sender, T message)
        {
            var interceptors = _serviceLocator.GetAll<IInterceptorOf<T>>().ToList();
            var consumers = _serviceLocator.GetAll<IConsumerOf<T>>();

            if (HasInterceptors(interceptors))
            {
                if (WasNotPublishedByInterceptor(interceptors, sender))
                {
                    interceptors.First().Intercept(message);
                    return;
                }
                if (WasNotPublishedByTheLastInterceptor(sender, interceptors))
                {
                    GetNextInterceptor(sender, interceptors).Intercept(message);
                    return;
                }
            }

            foreach (var consumer in consumers)
            {
                consumer.Consume(message);
            }
        }

        private bool HasInterceptors<T>(List<IInterceptorOf<T>> interceptors)
        {
            return interceptors.Count > 0;
        }

        private bool WasNotPublishedByTheLastInterceptor<T>(object sender, IEnumerable<IInterceptorOf<T>> interceptors)
        {
            return interceptors.Last() != sender;
        }

        private bool WasNotPublishedByInterceptor<T>(IEnumerable<IInterceptorOf<T>> interceptors, object sender)
        {
            return !interceptors.Any(i => i == sender);
        }

        private IInterceptorOf<T> GetNextInterceptor<T>(object sender, IList<IInterceptorOf<T>> interceptors)
        {
            for (int i = 0; i < interceptors.Count(); i++)
            {
                if (interceptors[i] == sender)
                {
                    return interceptors[i + 1];
                }
            }
            throw new ApplicationException("Should not happen");
        }
    }
}